package com.dyj.tiktok.handler;

import com.dyj.common.config.AgentConfiguration;
import com.dyj.common.domain.DyTikTokResult;
import com.dyj.common.domain.query.UserInfoQuery;
import com.dyj.common.utils.FieldUtils;
import com.dyj.tiktok.domain.vo.*;

import java.io.File;
import java.io.InputStream;

public class VideoHandler extends AbstractTikTokHandler {

    /**
     * 分片大小(5MB)
     * TikTok API分片要求：
     * 1. 每个块必须至少为5MB但不超过64MB，最后一个块除外
     * 2. 最后一个块可以大于chunk_size(最多128MB)以容纳尾随字节
     * 3. 总大小小于5MB的视频必须整体上传，chunk_size等于整个视频的字节大小
     * 4. 总大小大于64MB的视频必须分多个块上传
     * 5. 必须至少有1个块，最多有1000个块
     * 6. 文件块必须按顺序上传
     * 7. total_chunk_count应等于video_size除以chunk_size，向下舍入到最接近的整数
     * 
     * 当前实现策略：
     * 1. 文件小于10MB(标准分片大小的2倍)时，整个文件作为一次上传
     * 2. 文件大于等于10MB时，按5MB进行分片上传
     * 3. 分片数计算使用向下舍入方式(floor)
     */
    private final static long CHUNK_SIZE = 5 * 1024 * 1024;

    public VideoHandler(AgentConfiguration agentConfiguration) {
        super(agentConfiguration);
    }

    public DyTikTokResult<QueryVideoListVo> queryVideoList(String openId, Long cursor, Integer count) {
        UserInfoQuery query = new UserInfoQuery();
        query.setTenantId(agentConfiguration.getTenantId());
        query.setClientKey(agentConfiguration.getClientKey());
        query.setOpen_id(openId);
        return getVideoClient().queryVideoList(query, cursor, count, FieldUtils.getFieldsString(QueryVideoListVo.Videos.class));
    }

    /**
     * 初始化上传视频
     */
    public DyTikTokResult<InitUploadVideoRespVo> initUploadVideo(String openId, InitUploadVideoReqVo initUploadVideoReqVo) {
        UserInfoQuery query = new UserInfoQuery();
        query.setTenantId(agentConfiguration.getTenantId());
        query.setClientKey(agentConfiguration.getClientKey());
        query.setOpen_id(openId);
        return getVideoClient().initUploadVideo(query, initUploadVideoReqVo);
    }

    /**
     * 分片上传视频到草稿箱
     * @param uploadUrl 上传地址
     * @param fileInputStream 文件流
     * @param fileSize 上传文件大小
     * @return 上传结果
     */
    public Boolean uploadVideo(String uploadUrl, InputStream fileInputStream, Long fileSize) {
        try {
            // 总上传字节数
            long totalByteLength = fileSize;
            
            // 根据视频大小确定合适的分片大小
            long chunkSize;
            
            // 如果视频小于10MB(5MB的2倍)，则整个视频作为一个分片上传
            if (totalByteLength < 10 * 1024 * 1024) {
                chunkSize = totalByteLength;
                System.out.println("文件小于10MB，整体上传，分片大小: " + chunkSize + " 字节");
            } else {
                // 否则使用5MB作为分片大小
                chunkSize = CHUNK_SIZE;
                System.out.println("文件大于等于10MB，按5MB分片上传");
            }
            
            // 分片数量 - 向下舍入到最接近的整数
            int chunks = (int) Math.floor((double) totalByteLength / chunkSize);
            // 确保至少有1个分片
            chunks = Math.max(1, chunks);
            
            System.out.println("上传视频：总大小 " + totalByteLength + " 字节, 分片大小 " + chunkSize + " 字节, 分片数量 " + chunks);

            // 需要标记流的当前位置，以便后续分片读取
            // 注意：如果输入流不支持mark/reset，则需要创建一个支持此功能的包装流
            if (!fileInputStream.markSupported()) {
                System.out.println("警告: 输入流不支持标记/重置功能，可能会导致分片读取错误");
            }
            
            // 按分片进行上传
            for (int i = 0; i < chunks; i++) {
                // 当前分片的起始位置
                long firstByte = i * chunkSize;
                
                // 计算当前分片的结束位置
                long lastByte;
                if (i == chunks - 1) {
                    // 最后一个分片可能小于标准分片大小，包含剩余所有数据
                    lastByte = totalByteLength - 1; // 注意：索引从0开始
                } else {
                    lastByte = firstByte + chunkSize - 1; // 注意：索引从0开始
                }
                
                // 当前分片大小
                long currentChunkSize = lastByte - firstByte + 1;
                
                System.out.println("处理分片 " + (i + 1) + "/" + chunks + ": 字节范围 " + firstByte + "-" + lastByte);
                
                // 创建分片临时文件
                File tempChunkFile = createChunkFile(fileInputStream, firstByte, currentChunkSize);
                
                // 构建Content-Range头
                String contentRange = "bytes " + firstByte + "-" + lastByte + "/" + totalByteLength;
                
                // 上传当前分片
                getVideoClient().uploadVideo(uploadUrl, currentChunkSize, contentRange, tempChunkFile);

                // 删除临时文件
                if (tempChunkFile.exists()) {
                    boolean deleted = tempChunkFile.delete();
                    if (!deleted) {
                        System.out.println("警告: 临时文件 " + tempChunkFile.getAbsolutePath() + " 删除失败");
                        // 添加到JVM退出时删除的文件列表
                        tempChunkFile.deleteOnExit();
                    }
                }
                
                System.out.println("上传进度: " + (i + 1) + "/" + chunks + " 分片");
            }
            
            return true;
        } catch (Exception e) {
            System.err.println("视频上传失败: " + e.getMessage());
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
            } catch (Exception e) {
                System.err.println("关闭文件流失败: " + e.getMessage());
            }
        }
    }
    
    /**
     * 创建分片临时文件
     * @param inputStream 源文件输入流
     * @param startPos 开始位置
     * @param length 长度
     * @return 分片临时文件
     */
    private File createChunkFile(InputStream inputStream, long startPos, long length) throws Exception {
        java.io.FileOutputStream fos = null;
        
        try {
            // 创建临时文件
            File tempFile = File.createTempFile("chunk_", ".tmp");
            fos = new java.io.FileOutputStream(tempFile);
            
            // 跳过前面的字节
            long bytesSkipped = 0;
            while (bytesSkipped < startPos) {
                long skipped = inputStream.skip(startPos - bytesSkipped);
                if (skipped <= 0) {
                    break;
                }
                bytesSkipped += skipped;
            }
            
            // 写入数据
            byte[] buffer = new byte[1024 * 1024]; // 1MB缓冲区
            long remainingBytes = length;
            
            while (remainingBytes > 0) {
                int bytesToRead = (int) Math.min(buffer.length, remainingBytes);
                int bytesRead = inputStream.read(buffer, 0, bytesToRead);
                
                if (bytesRead == -1) {
                    break;
                }
                
                fos.write(buffer, 0, bytesRead);
                remainingBytes -= bytesRead;
            }
            
            fos.flush();
            return tempFile;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (Exception e) {
                    // 忽略
                }
            }
            // 注意：不关闭inputStream，由调用者负责关闭
        }
    }

    public DyTikTokResult<UploadImageRespVo> uploadImage(String openId, UploadImageReqVo uploadImageReqVo) {
        UserInfoQuery query = new UserInfoQuery();
        query.setTenantId(agentConfiguration.getTenantId());
        query.setClientKey(agentConfiguration.getClientKey());
        query.setOpen_id(openId);
        return getVideoClient().uploadImage(query, uploadImageReqVo);
    }

    public DyTikTokResult<QueryPostStatusRespVo> queryPostStatus(String openId, String publishId) {
        UserInfoQuery query = new UserInfoQuery();
        query.setTenantId(agentConfiguration.getTenantId());
        query.setClientKey(agentConfiguration.getClientKey());
        query.setOpen_id(openId);
        return getVideoClient().queryPostStatus(query, publishId);
    }
}
